package ru.spb.osll.myfit.server.fixtureiterator;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Enumeration;
import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.regex.Pattern;

import ru.spb.osll.myfit.util.ReflectionUtils;
import fit.Fixture;

public class ClasspathIterator {
	private static final String JAR = ".jar";
	private static final String ZIP = ".zip";
	private static final String CLASS = ".class";
	
	private URLClassLoader m_classLoader;
	private FileFactory m_fileFactory;
	private URL[] m_urls;
 
	public ClasspathIterator(URLClassLoader classLoader, FileFactory fileFactory, URL[] urls) {
		m_classLoader = classLoader;
		m_fileFactory = fileFactory;
		m_urls = urls;
	}

	public void iterateFixtures(FixtureIteratorListener listener) {
		for (URL url :  m_urls) {
			File file = m_fileFactory.createFile(url.getFile());
			vistUrl(file, m_classLoader, listener, url.getFile());
		}
	}

	private void vistUrl(File urlFile, URLClassLoader classLoader,
			FixtureIteratorListener listener, String classpathDir) {
		if (urlFile.isFile()) {
			if (urlFile.getName().endsWith(JAR)
					|| urlFile.getName().endsWith(ZIP)) {
				vistJar(urlFile, classLoader, listener);
			}
			if (urlFile.getName().endsWith(CLASS)) {
				vistClass(urlFile.getPath(), classLoader, listener,
						classpathDir);
			}
		} else if (urlFile.isDirectory()) {
			visitDir(urlFile, classLoader, listener, classpathDir);
		}
	}

	private void vistJar(File urlFile, URLClassLoader classLoader,
			FixtureIteratorListener listener) {
		try {

			JarFile jarFile = m_fileFactory.createJar(urlFile);
			Enumeration<JarEntry> entries = jarFile.entries();
			while (entries.hasMoreElements()) {
				String name = entries.nextElement().getName();
				if (name.endsWith(CLASS)) {
					vistClass(name, classLoader, listener, "");
				}
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	private void visitDir(File urlFile, URLClassLoader classLoader,
			FixtureIteratorListener listener, String classpathDir) {
		for (File entry : urlFile.listFiles()) {
			vistUrl(entry, classLoader, listener, classpathDir);
		}
	}

	private void vistClass(String name, URLClassLoader classLoader,
			FixtureIteratorListener listener, String classpathDir) {
		String className = name;

		if (classpathDir.length() > 0) {
			className = name
					.substring(classpathDir.length() + 1, name.length());
		}

		className = className.replaceAll(Pattern.quote("/"), "\\.");
		className = className.replaceAll(Pattern.quote("\\"), "\\.");
		className = className.replaceAll(CLASS, "");
		try {
			Class<?> clazz = classLoader.loadClass(className);

			if (isFixture(clazz)) {
				listener.listen(clazz);
			}
		} catch (NoClassDefFoundError error) {
		} catch (ClassNotFoundException e) {
		}

	}

	private boolean isFixture(Class<?> clazz) {
		Set<String> hierarhy = ReflectionUtils.getClassHierarhy(clazz);
		return hierarhy.contains(Fixture.class.getCanonicalName());
	}
}
